using System;
using System.Collections;
using System.IO;
using System.Text;
using fit;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TestUtils
{
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
    public class TestPrerequisiteAttribute : Attribute
    {
        private readonly string prerequisiteName;

        public TestPrerequisiteAttribute(string prerequisiteFileName)
        {
            prerequisiteName = prerequisiteFileName;
        }

        public string PrerequisiteName
        {
            get { return prerequisiteName; }
        }
    }

    public abstract class AttributedFitRunner
    {
        protected Hashtable dictionary;

        public AttributedFitRunner()
        {
            dictionary = CreateDictionary();
            ContextManager.TestContext = CreateContext();
        }

        private int RunTheTest(string inFile, string outFile, Hashtable fixtureDictionary)
        {
            TestPrerequisiteAttribute[] prerequisiteAttributes =
                (TestPrerequisiteAttribute[])GetType().GetCustomAttributes(typeof(TestPrerequisiteAttribute), true);
            
            foreach (TestPrerequisiteAttribute testPrerequisiteAttribute in prerequisiteAttributes)
            {
                string prerequisiteFileName = InFolder + testPrerequisiteAttribute.PrerequisiteName;
                inFile = InsertPrerequisiteIntoFile(prerequisiteFileName, inFile);
            }

            if (File.Exists(outFile))
            {
                File.SetAttributes(outFile, FileAttributes.Normal);
            }
            FileRunner runner = new FileRunner();
            runner.Dictionary = fixtureDictionary;
            string directory = Environment.CurrentDirectory;

            runner.args(new string[] {inFile, outFile, directory});
            runner.process();
            runner.output.Close();

            File.SetAttributes(outFile, FileAttributes.ReadOnly);

            return runner.fixture.counts.exceptions > 0 ? runner.fixture.counts.exceptions : runner.fixture.counts.wrong;
        }

        private string InsertPrerequisiteIntoFile(string prerequisiteFileName, string mainTestFileName)
        {
            string mainTest;
            string prerequisiteTest;
            using (StreamReader mainTestStream = new StreamReader(mainTestFileName))
            {
                mainTest = mainTestStream.ReadToEnd();
            }
            using (StreamReader prerequisiteTestStream = new StreamReader(prerequisiteFileName))
            {
                prerequisiteTest = prerequisiteTestStream.ReadToEnd();
            }

            int mainTestBodyStart = mainTest.ToLower().IndexOf("<body>");
            int prerequisiteTestBodyStart = prerequisiteTest.ToLower().IndexOf("<body>") + 6;
            int prerequisiteTestBodyEnd = prerequisiteTest.ToLower().IndexOf("</body>");
            string prerequisiteTestBody =
                prerequisiteTest.Substring(prerequisiteTestBodyStart,
                                           prerequisiteTestBodyEnd - prerequisiteTestBodyStart);


            StringBuilder mixedTextBuilder = new StringBuilder();
            mixedTextBuilder.Append(mainTest.Substring(0, mainTestBodyStart+6));
            mixedTextBuilder.Append("<H2>Prerequisite:</H2>");
            mixedTextBuilder.Append(prerequisiteTestBody);
            mixedTextBuilder.Append("<HR>");
            mixedTextBuilder.Append(mainTest.Substring(mainTestBodyStart+6));

            string tempFileName = Path.GetTempFileName();
            using (StreamWriter mixedTest = new StreamWriter(tempFileName, false))
            {
                mixedTest.Write(mixedTextBuilder.ToString());
            }

            return tempFileName;
        }

        protected virtual Context CreateContext()
        {
            return new Context();
        }

        [TestMethod]
        public void TestRun()
        {
            Assert.AreEqual(0, RunTheTest(FullInputName, FullOutputName, dictionary));
        }

        protected virtual Hashtable CreateDictionary()
        {
            return new Hashtable();
        }

        private string FullOutputName
        {
            get { return OutFolder + (ResultPrefix == String.Empty ? String.Empty : ResultPrefix + '_') + TestName; }
        }

        private string FullInputName
        {
            get { return InFolder + TestName; }
        }

        protected virtual string ResultPrefix
        {
            get { return string.Empty; }
        }

        protected abstract string TestName { get; }
        protected abstract string InFolder { get; }
        protected abstract string OutFolder { get; }
    }
}